package org.hxzon.project;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hxzon.util.OgnlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class Hibernate3Dao extends HibernateDaoSupport implements Dao {

    @SuppressWarnings("rawtypes")
    private final Map<Class, List<String>> stringFields = new HashMap<Class, List<String>>();
    @SuppressWarnings("rawtypes")
    private final Map<Class, String> fromString = new HashMap<Class, String>();

    public Hibernate3Dao() {
    }

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    public int executeSql(final String sql) {
        return super.getHibernateTemplate().execute(new HibernateCallback<Integer>() {
            public Integer doInHibernate(Session session) throws HibernateException, SQLException {

                logger.debug(sql);
                Query query = session.createSQLQuery(sql);
                return query.executeUpdate();
            }
        });
    }

    public int execute(final String hql) {
        return super.getHibernateTemplate().execute(new HibernateCallback<Integer>() {
            public Integer doInHibernate(Session session) throws HibernateException, SQLException {

                logger.debug(hql);
                Query query = session.createQuery(hql);
                return query.executeUpdate();
            }
        });
    }

    public List query(final String hql, final long first, final long size) {
        return super.getHibernateTemplate().executeFind(new HibernateCallback<List>() {
            public List doInHibernate(Session session) throws HibernateException, SQLException {

                logger.debug(hql);
                Query query = session.createQuery(hql);
                if (first > -1 && size > 0) {
                    query.setFirstResult((int) first);
                    query.setMaxResults((int) size);
                }
                return query.list();
            }
        });
    }

    public List query(String hql) {
        return query(hql, -1, -1);
    }

    // save delete
    public void delete(Object model) {
        if (model == null) {
            return;
        }
        super.getHibernateTemplate().delete(model);

    }

    public <M> void delete(Class<M> clazz, String id) {
        if (id == null || id.isEmpty()) {
            return;
        }
        delete(findById(clazz, id));
    }

    public <M> void deleteAll(final Class<M> clazz) {
        super.getHibernateTemplate().execute(new HibernateCallback<Object>() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.createQuery(" delete " + clazz.getName());
                query.executeUpdate();
                return null;
            }
        });
    }

    public <M> void deleteAll(Collection<M> models) {
        if (models == null || models.isEmpty()) {
            return;
        }
        super.getHibernateTemplate().deleteAll(models);
    }

    // save
    public void save(Object model) {
        if (model == null) {
            return;
        }
        if (model instanceof BaseEntity) {
            BaseEntity m = (BaseEntity) model;
            m.updateTimeInfo();
        }
        super.getHibernateTemplate().saveOrUpdate(model);
    }

    public <M> void saveAll(Collection<M> models) {
        if (models == null || models.isEmpty()) {
            return;
        }
        for (Object model : models) {
            if (model instanceof BaseEntity) {
                BaseEntity m = (BaseEntity) model;
                m.updateTimeInfo();
            }
        }
        super.getHibernateTemplate().saveOrUpdateAll(models);
    }

    public void add(Object model) {
        if (model == null) {
            return;
        }
        if (model instanceof BaseEntity) {
            BaseEntity m = (BaseEntity) model;
            m.updateTimeInfo();
        }
        super.getHibernateTemplate().save(model);
    }

    public <M> void addAll(Collection<M> models) {
        if (models == null || models.isEmpty()) {
            return;
        }
        for (Object model : models) {
            if (model instanceof BaseEntity) {
                BaseEntity m = (BaseEntity) model;
                m.updateTimeInfo();
            }
        }
        super.getHibernateTemplate().saveOrUpdateAll(models);
    }

    public void update(Object model) {
        if (model == null) {
            return;
        }
        if (model instanceof BaseEntity) {
            BaseEntity m = (BaseEntity) model;
            m.updateTimeInfo();
        }
        super.getHibernateTemplate().update(model);
    }

    public <M> void updateAll(Collection<M> models) {
        if (models == null || models.isEmpty()) {
            return;
        }
        for (Object model : models) {
            if (model instanceof BaseEntity) {
                BaseEntity m = (BaseEntity) model;
                m.updateTimeInfo();
            }
        }
        super.getHibernateTemplate().saveOrUpdateAll(models);
    }

    // get
    public <M> M findById(Class<M> clazz, String id) {
        if (id == null || id.isEmpty()) {
            return null;
        }
        return super.getHibernateTemplate().get(clazz, id);
    }

    public <M> M findByProperty(Class<M> clazz, String fieldName, Object fieldValue) {
        if (fieldName == null || fieldName.isEmpty() || fieldValue == null) {
            return null;
        }
        List<M> list = findList(clazz, fieldName, fieldValue, -1, -1);
        if (list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    @SuppressWarnings("unchecked")
    private <M> List<M> doGetList(final Class<M> clazz, final String whereAndOrderBy, final long first, final long size) {
        return super.getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                StringBuilder sb = new StringBuilder(getFromString(clazz));
                if (whereAndOrderBy != null && !whereAndOrderBy.isEmpty()) {
                    sb.append(whereAndOrderBy);
                }
                if (sb.indexOf("order by") == -1 && !getDefaultOrderByString(clazz).isEmpty()) {
                    sb.append(getDefaultOrderByString(clazz));
                }
                logger.debug(sb.toString());
                Query query = session.createQuery(sb.toString());
                if (first > -1 && size > 0) {
                    query.setFirstResult((int) first);
                    query.setMaxResults((int) size);
                }
                return (List<M>) query.list();
            }
        });
    }

    // all
    public <M> List<M> findAll(Class<M> clazz) {
        return doGetList(clazz, null, -1, -1);
    }

    public <M> List<M> findAll(Class<M> clazz, long first, long size) {
        return doGetList(clazz, null, first, size);
    }

    // list
    public <M> M findOne(Class<M> clazz, String whereAndOrderBy, boolean checkOnlyOne) {
        List<M> result = doGetList(clazz, whereAndOrderBy, -1, checkOnlyOne ? -1 : 1);
        if (checkOnlyOne && result.size() > 1) {
            logger.warn("not only one" + clazz.getCanonicalName() + ":" + whereAndOrderBy);
        }
        if (result.isEmpty()) {
            return null;
        }
        return result.get(0);
    }

    public <M> M findOne(Class<M> clazz, String whereAndOrderBy) {
        return findOne(clazz, whereAndOrderBy, true);
    }

    public <M> M findFirst(Class<M> clazz, String whereAndOrderBy) {
        return findOne(clazz, whereAndOrderBy, false);
    }

    public <M> List<M> findList(Class<M> clazz, String whereAndOrderBy) {
        return doGetList(clazz, whereAndOrderBy, -1, -1);
    }

    public <M> List<M> findList(Class<M> clazz, String whereAndOrderBy, long first, long size) {
        return doGetList(clazz, whereAndOrderBy, first, size);
    }

    public <M> List<M> findList(Class<M> clazz, String fieldName, Object fieldValue, long first, long size) {
        if (fieldValue instanceof String) {
            fieldValue = "'" + OgnlUtil.escape((String) fieldValue) + "'";
        }
        return doGetList(clazz, " where p." + fieldName + " = " + fieldValue, first, size);
    }

    @SuppressWarnings("unchecked")
    public <M> List<M> doLike(final M example, List<String> fields, final long first, final long size) {
        final List<String> fields2 = new ArrayList<String>();
        Object value = null;
        for (int i = fields.size() - 1; i >= 0; i--) {
            value = OgnlUtil.getValue(example, fields.get(i));
            if (value != null && value instanceof String && !((String) value).isEmpty()) {
                fields2.add(fields.get(i));
            }
        }
        return super.getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Class<?> clazz = example.getClass();
                StringBuilder hql = new StringBuilder(getFromString(clazz));
                appendWhereString(fields2, example, hql);
                if (hql.indexOf("order by") == -1 && !getDefaultOrderByString(clazz).isEmpty()) {
                    hql.append(getDefaultOrderByString(clazz));
                }
                logger.debug(hql.toString());
                Query query = session.createQuery(hql.toString());
                if (first > -1 && size > 0) {
                    query.setFirstResult((int) first);// fixme
                    query.setMaxResults((int) size);
                }
                return (List<M>) query.list();
            }
        });
    }

    public <M> List<M> findLikeExample(final M example, List<String> fields, final long first, final long size) {
        return doLike(example, fields, first, size);
    }

    public <M> List<M> findLikeByExample(final M modelExample, final long first, final long size) {
        return findLikeExample(modelExample, getStringFields(modelExample.getClass()), first, size);
        // return super.getHibernateTemplate().executeFind(new
        // HibernateCallback() {
        // public Object doInHibernate(Session session) throws
        // HibernateException, SQLException {
        // Example
        // example=Example.create(modelExample).setPropertySelector(stringProperty).ignoreCase().enableLike(MatchMode.ANYWHERE);
        //              
        // Criteria query =
        // session.createCriteria(modelExample.getClass()).add(example);
        // if (first > -1 && size > 0) {
        // query.setFirstResult((int) first);// fixme
        // query.setMaxResults((int) size);
        // }
        // return (List<M>) query.list();
        // }
        // });
    }

    @SuppressWarnings("unchecked")
    public <M> List<M> doLike(final Class<M> clazz, final String like, final List<String> fields, final long first, final long size) {
        return super.getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                StringBuilder hql = new StringBuilder(getFromString(clazz));
                appendWhereString(fields, like, hql);
                if (hql.indexOf("order by") == -1 && !getDefaultOrderByString(clazz).isEmpty()) {
                    hql.append(getDefaultOrderByString(clazz));
                }
                logger.debug(hql.toString());
                Query query = session.createQuery(hql.toString());
                if (first > -1 && size > 0) {
                    query.setFirstResult((int) first);// fixme
                    query.setMaxResults((int) size);
                }
                return (List<M>) query.list();
            }
        });
    }

    public <M> List<M> findLike(Class<M> clazz, final String like, final List<String> fields, final long first, final long size) {
        return doLike(clazz, like, fields, first, size);
    }

    public <M> List<M> findLike(Class<M> clazz, String like, long first, long size) {
        return findLike(clazz, like, getStringFields(clazz), first, size);
    }

    // count
    private <M> long doCount(Class<M> clazz, String where) {
        StringBuilder hql = new StringBuilder("select count(p.id) ");
        hql.append(getFromString(clazz));
        if (where != null && !where.isEmpty()) {
            hql.append(where);
        }
        List list = super.getHibernateTemplate().find(hql.toString());
        return (Long) list.get(0);

    }

    public <M> long countAll(Class<M> clazz) {
        return doCount(clazz, null);
    }

    public <M> long countList(Class<M> clazz, String where) {
        return doCount(clazz, where);
    }

    public <M> long countList(Class<M> clazz, String fieldName, Object fieldValue) {
        if (fieldValue instanceof String) {
            fieldValue = "'" + OgnlUtil.escape((String) fieldValue) + "'";
        }
        return doCount(clazz, " where p." + fieldName + " = " + fieldValue);
    }

    public <M> long countLikeExample(final M example, final List<String> fields) {
        final List<String> fields2 = new ArrayList<String>();
        Object value = null;
        for (int i = fields.size() - 1; i >= 0; i--) {
            value = OgnlUtil.getValue(example, fields.get(i));
            if (value != null && value instanceof String && !((String) value).isEmpty()) {
                fields2.add(fields.get(i));
            }
        }
        StringBuilder hql = new StringBuilder("select count(p.id) ");
        hql.append(getFromString(example.getClass()));
        appendWhereString(fields2, example, hql);
        logger.debug(hql.toString());
        List list = super.getHibernateTemplate().find(hql.toString());
        return (Long) list.get(0);
    }

    public <M> long countLikeByExample(final M modelExample) {
        return countLikeExample(modelExample, getStringFields(modelExample.getClass()));
        // return super.getHibernateTemplate().execute(new HibernateCallback() {
        // public Object doInHibernate(Session session) throws
        // HibernateException, SQLException {
        // Example
        // example=Example.create(modelExample).setPropertySelector(stringProperty).ignoreCase().enableLike(MatchMode.ANYWHERE);
        //              
        // Criteria query =
        // session.createCriteria(modelExample.getClass()).add(example).setProjection(new
        // RowCountProjection());
        // return (Long) query.list().get(0);
        // }
        // });
    }

    public <M> long countLike(Class<M> clazz, String like, List<String> fields) {
        StringBuilder hql = new StringBuilder("select count(p.id) ");
        hql.append(getFromString(clazz));
        appendWhereString(fields, like, hql);
        List list = super.getHibernateTemplate().find(hql.toString());
        return (Long) list.get(0);
    }

    public <M> long countLike(Class<M> clazz, String like) {
        return countLike(clazz, like, getStringFields(clazz));
    }

    //search
//  @SuppressWarnings("unchecked")
    public <M> List<M> search(Class<M> clazz, String search, long first, long size) {
//      QueryParser parser = new QueryParser(Version.LUCENE_30,"search", new StandardAnalyzer(Version.LUCENE_30));//new ChineseAnalyzer());
//      org.apache.lucene.search.Query luceneQuery;
//      try {
//          luceneQuery = parser.parse(search);
//          FullTextSession s = Search.getFullTextSession(getSession());
//          FullTextQuery query = s.createFullTextQuery(luceneQuery, clazz);
//          if (first > -1 && size > 0) {
//              query.setFirstResult((int) first);// fixme
//              query.setMaxResults((int) size);
//          }
//          return (List<M>)query.list();
//      } catch (ParseException e) {
//          return new ArrayList<M>();
//      }
        return null;
    }

    public <M> long countSearch(Class<M> clazz, String search) {
//      QueryParser parser = new QueryParser(Version.LUCENE_30,"search", new StandardAnalyzer(Version.LUCENE_30));//new ChineseAnalyzer());
//      org.apache.lucene.search.Query luceneQuery;
//      try {
//          luceneQuery = parser.parse(search);
//          FullTextSession s = Search.getFullTextSession(getSession());
//          FullTextQuery query = s.createFullTextQuery(luceneQuery, clazz);
//          return query.getResultSize();
//      } catch (ParseException e) {
//          return 0;
//      }
        return 0;
    }

    private String appendWhereString(List<String> fields, String like, StringBuilder hql) {
        like = OgnlUtil.escape(like);
        if (!fields.isEmpty()) {// fixme not return String
            hql.append(" where p.").append(fields.get(0)).append(" like '%").append(like).append("%'");
        }
        for (int i = 1; i < fields.size(); i++) {
            hql.append(" or p.").append(fields.get(i)).append(" like '%").append(like).append("%'");
        }
        return hql.toString();
    }

    private <M> void appendWhereString(List<String> fields, M example, StringBuilder hql) {
        if (!fields.isEmpty()) {
            hql.append(" where p.").append(fields.get(0)).append(" like '%").append(OgnlUtil.getStringValueOrEmpty(example, fields.get(0)).replace("'", "''")).append("%'");
        }
        for (int i = 1; i < fields.size(); i++) {
            hql.append(" and p.").append(fields.get(i)).append(" like '%").append(OgnlUtil.getStringValueOrEmpty(example, fields.get(i)).replace("'", "''")).append("%'");
        }
    }

    protected String getDefaultOrderByString(Class<?> clazz) {
        return "";
    }

    private <M> String getFromString(Class<M> clazz) {
        String s = fromString.get(clazz);
        if (s == null) {
            s = " from " + clazz.getName() + " as p ";
            fromString.put(clazz, s);
        }
        return s;
    }

    protected <M> List<String> getStringFields(Class<M> clazz) {
        List<String> ls = stringFields.get(clazz);
        if (ls == null) {
            ls = OgnlUtil.getAllStringProperty(clazz);
            stringFields.put(clazz, ls);
        }
        return ls;
    }

    // static final class StringPropertySelecter implements PropertySelector{
    //
    // public boolean include(Object propertyValue, String propertyName, Type
    // type) {
    // if(propertyValue ==null || !(propertyValue instanceof String)){
    // return false;
    // }
    // if(((String)propertyValue).isEmpty()){
    // return false;
    // }
    // return true;
    // }
    //      
    // }
    // private static final PropertySelector stringProperty=new
    // StringPropertySelecter();

}
